/* 
 * File:   Enviroment.h
 * Author: flai0014
 *
 * Created on March 30, 2012, 11:48 AM
 */

#ifndef _ENVIROMENT_H
#define	_ENVIROMENT_H
#include "EnvCell.h"
#include <vector>
#include <pthread.h>
#include <iostream>
#include <cstdlib>
#define MAX_AGENTS 5 
#define VERTICAL_SIZE 15
#define HORIZONTAL_SIZE 20
#define NUM_OF_PARTICLES 75
#define PARTICLE_CHECK ((NUM_OF_PARTICLES+1)*NUM_OF_PARTICLES/2)
                                                               
using namespace std;


class Enviroment {
public:
    Enviroment();
    virtual ~Enviroment();
    
    /* Retrieves a reference to the enviroment cell by its location
     */
    static EnvCell* getCell(int cellNumber){
        int verticalNum = cellNumber/VERTICAL_SIZE;
        int horizontalNum = cellNumber%VERTICAL_SIZE;
        return m_enviroment[verticalNum][horizontalNum];
    }
    
    /* This call checks the particles and there unique identifier number to
     * make sure there are no doubles.
     */
    void checkParticles();
    
    /* This function returns a true if an agent needs to wait for a count
     * so the agent needs to drop his particle and wait for an all clear
     * from the enviroment  
     */
    static bool countWait(){
        return count_Wait;    
    }
    
    /* This returns a true if the enviroment needs to print the enviroment to
     * the screen. The agent can hold his particle and waits of all clear.
     */
    static bool printWait(){
        return print_Wait;
    }
    
    /* This is a call for the enviroment to print itself. It sets the print wait
     * variable and the agents then go into a waiting state. The agents should
     * restart after this call
     */
    void printEnviroment();
    
    /* This function returns a new location for an agent based on the direction
     * it wants to go and its current location. If the agent is on the border 
     * this sends the agent in the opposite direction.
     */
    static int move(int direction, int curLocation){
        switch(direction){
            case 0:
                if(curLocation - HORIZONTAL_SIZE < 0)
                    return curLocation + HORIZONTAL_SIZE;
                else
                    return curLocation - HORIZONTAL_SIZE;
            case 1:
                if(curLocation%HORIZONTAL_SIZE == 0)
                    return curLocation + 1;
                else
                    return curLocation - 1;
            case 2:
                if(curLocation%HORIZONTAL_SIZE == HORIZONTAL_SIZE - 1)
                    return curLocation - 1;
                else
                    return curLocation + 1;                
            case 3:
                if(curLocation + HORIZONTAL_SIZE >= 
                                        HORIZONTAL_SIZE*VERTICAL_SIZE)
                    return curLocation - HORIZONTAL_SIZE;
                else
                    return curLocation + HORIZONTAL_SIZE;
                
        }
    }
    
    void startAgents();
    
    void waitAgents();
    
    /* Mutex lock needed to access anything in the enviroment
     */
    static pthread_mutex_t m_mutex;
    
    /* The count condition variable that the enviroment waits on while the
     * agents are checking in for a count and a print
     */
    static pthread_cond_t count_condition_var;
    static pthread_cond_t wait_condition_var;
    
    
    /* This function starts a count of agents after they are requested to wait
     */
    static void agentCheckIn(){
        agentCount++;
    }
    
    /* Test function for running the program */
    int test(){
        int cmd = 8;
        bool toggle = false;
        for (;;) {
            pthread_mutex_lock(&m_mutex);
            cout << "\nmain: Enter command:\n";
            cout << "   0) Exit\n";
            cout << "   1) Print Environment\n";
            cout << "   2) Pause Agent threads\n";
            cout << "   3) Resume Agent threads\n";
            cout << "   4) Count Particles\n";
            cout << "   5) Toggle sleep for agents\n";

            cout << "Enter command: ";
            cout.flush();
            pthread_mutex_unlock(&m_mutex);
            cin >> cmd;

            switch (cmd) {
                case 0:                
                    return 0;
                    break;

                case 1: 
                    printEnviroment();
                    break;

                case 2:
                    waitAgents();
                    toggle = false;
                    break;

                case 3:
                    startAgents();
                    toggle = true;
                    break;

                case 4:
                    checkParticles();
                    break;

                case 5:
                    if(toggle){
                        waitAgents();
                        toggle = false;
                    }else{
                        startAgents();
                        toggle = true;
                    }
                    break;

                default:
                    cout << "main: Bad command!\n";
                    break;
            } // end switch
            if(cmd == 0)
                break;
        } // end for   
    }
    
private:
    static int agentCount;
    static vector<vector<EnvCell *> > m_enviroment;
    int m_numOfParticles;
    static bool count_Wait;
    static bool print_Wait;
};


#endif	/* ENVIROMENT_H */

